package com.lyl.security.module.security.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.lyl.security.common.utils.RedisUtil;
import com.lyl.security.module.security.model.constant.Constants;
import com.lyl.security.module.security.model.constant.JwtConstant;
import com.lyl.security.module.security.model.details.LoginUser;
import com.lyl.security.module.security.model.properties.SecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * @author Mr.Luo
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TokenService {
    private final SecurityProperties securityProperties;

    public String setToken(Authentication authenticate) {
        if (authenticate == null) {
            return null;
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        loginUser.setToken(IdUtil.fastUUID());
        refreshToken(loginUser);
        return createToken(loginUser.getToken());
    }

    /**
     * 生成token
     *
     * @param tokenId tokenId
     * @return token
     */
    public String createToken(String tokenId) {
        return JWTUtil.createToken(MapUtil.builder(JwtConstant.JWT_TOKEN_PARAM, (Object) tokenId).build(), securityProperties.getToken().getSecret().getBytes(StandardCharsets.UTF_8));
    }

    public LoginUser getLoginUser(HttpServletRequest request) {
        String token = getToken(request);
        if (CharSequenceUtil.isNotBlank(token)) {
            //1. 解析token
            String tokenId = parseToken(token);
            //2. 从redis中获取用户数据
            Object userObj = RedisUtil.get(getKey(tokenId));
            if (ObjectUtil.isNotEmpty(userObj)) {
                LoginUser loginUser = JSONUtil.toBean(JSONUtil.toJsonStr(userObj), LoginUser.class);
                //3. 判断数据合法性
                verifyToken(loginUser);
                return loginUser;
            }
        }
        return null;
    }

    private String parseToken(String token) {
        return Convert.toStr(JWTUtil.parseToken(token).getPayload(JwtConstant.JWT_TOKEN_PARAM));
    }

    private void verifyToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= securityProperties.getToken().getMaxRefreshTokenTime()) {
            //刷新token
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新token
     *
     * @param loginUser 登录用户
     */
    public void refreshToken(LoginUser loginUser) {
        long expireTime;
        long loginTime = System.currentTimeMillis();
        expireTime = loginTime + securityProperties.getToken().getExpiredTime() * Constants.ONE_MINUTE;
        loginUser.setLoginTime(loginTime);
        loginUser.setExpireTime(expireTime);
        RedisUtil.set(getKey(loginUser.getToken()), loginUser, securityProperties.getToken().getExpiredTime(), TimeUnit.MINUTES);
    }

    public String getKey(String tokenId) {
        return JwtConstant.REDIS_TOKEN_KEY_PREFIX + tokenId;
    }

    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(securityProperties.getToken().getHeader());
        if (CharSequenceUtil.isNotBlank(token) && token.startsWith(securityProperties.getToken().getPrefix() + StrPool.C_SPACE)) {
            token = token.replace(securityProperties.getToken().getPrefix() + StrPool.C_SPACE, "");
        }
        return token;
    }
}
